/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.zkex.simple;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.lang.Streams;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.DoReportRequestMessage;
import cn.easyplatform.messages.vos.ReportVo;
import cn.easyplatform.messages.vos.executor.EndVo;
import cn.easyplatform.messages.vos.executor.ErrorVo;
import cn.easyplatform.spi.service.ComponentService;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.web.WebApps;
import cn.easyplatform.web.contexts.Contexts;
import cn.easyplatform.web.dialog.ProgressDialog;
import cn.easyplatform.web.ext.ComponentBuilder;
import cn.easyplatform.web.ext.Destroyable;
import cn.easyplatform.web.ext.zul.JReport;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.BackendException;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.task.zkex.ReportSupport;
import io.keikai.api.Exporter;
import io.keikai.api.Exporters;
import io.keikai.api.Importer;
import io.keikai.api.Importers;
import io.keikai.api.model.Book;
import io.keikai.api.model.Sheet;
import io.keikai.ui.Spreadsheet;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.export.*;
import net.sf.jasperreports.engine.export.oasis.JROdsExporter;
import net.sf.jasperreports.engine.export.oasis.JROdtExporter;
import net.sf.jasperreports.engine.export.ooxml.JRDocxExporter;
import net.sf.jasperreports.engine.export.ooxml.JRPptxExporter;
import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter;
import net.sf.jasperreports.export.*;
import net.sf.jasperreports.j2ee.servlets.ImageServlet;
import net.sf.jasperreports.web.util.WebHtmlResourceHandler;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.util.media.AMedia;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Components;
import org.zkoss.zk.ui.Sessions;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zkex.zul.Pdfviewer;
import org.zkoss.zkmax.zul.Filedownload;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class JReportBuilder implements ComponentBuilder, Destroyable,
        ReportSupport, EventListener<Event> {

    private final static Logger log = LoggerFactory.getLogger(JReportBuilder.class);

    private final static String IMAGE_PATTERN = WebApps.getContextPath() + "/servlets/images?image={0}";

    private OperableHandler mainTaskHandler;

    private JReport report;

    private Spreadsheet spreadsheet;

    private Pdfviewer pdfviewer;

    private Object reportContent;

    private ProgressDialog mc;

    public JReportBuilder(OperableHandler mainTaskHandler, JReport report) {
        this.mainTaskHandler = mainTaskHandler;
        this.report = report;
    }

    @Override
    public Component build() {
        if (Strings.isBlank(report.getEntity())) {
            // 不抛错，不处理
            report.detach();
            report = null;
            return null;
            // throw new EasyplatformWithLabelKeyException(
            // "component.property.not.found", "<report>", "entity");
        }
        if (Strings.isBlank(report.getId()))
            throw new EasyPlatformWithLabelKeyException(
                    "component.property.not.found", "<report>", "id");
        if (report.isImmediate())
            reload();
        return report;
    }

    @Override
    public void destory() {
        Sessions.getCurrent().removeAttribute(
                ImageServlet.DEFAULT_JASPER_PRINT_SESSION_ATTRIBUTE);
    }

    @Override
    public void exportAll(String type, Object... exportHeaders) {
        export(type, exportHeaders);
    }

    @Override
    public void export(String type, Object... exportHeaders) {
        if (reportContent == null)
            return;
        if (reportContent instanceof JasperPrint) {
            JasperPrint jasperPrint = (JasperPrint) reportContent;
            ByteArrayOutputStream bos = null;
            try {
                String contentType = null;
                if (type.equalsIgnoreCase("pdf")) {
                    JRPdfExporter exporter = new JRPdfExporter();
                    exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
                    SimpleOutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(
                            bos = new ByteArrayOutputStream());
                    try {
                        exporter.setExporterOutput(exporterOutput);
                        exporter.exportReport();
                    } finally {
                        exporterOutput.close();
                        exporterOutput = null;
                        exporter = null;
                    }
                    contentType = "application/pdf";
                } else if (type.equalsIgnoreCase("csv")) {
                    JRCsvExporter exporter = new JRCsvExporter();
                    exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
                    SimpleWriterExporterOutput exporterOutput = new SimpleWriterExporterOutput(
                            bos = new ByteArrayOutputStream(), "utf-8");
                    try {
                        exporter.setExporterOutput(exporterOutput);
                        SimpleCsvExporterConfiguration configuration = new SimpleCsvExporterConfiguration();
                        configuration.setFieldDelimiter(",");
                        configuration.setRecordDelimiter(";\r\n");
                        exporter.setConfiguration(configuration);
                        exporter.exportReport();
                    } finally {
                        exporterOutput.close();
                        exporterOutput = null;
                        exporter = null;
                    }
                    contentType = "application/octet-stream";
                } else if (type.equalsIgnoreCase("rtf")) {
                    JRRtfExporter exporter = new JRRtfExporter();
                    exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
                    SimpleWriterExporterOutput exporterOutput = new SimpleWriterExporterOutput(
                            bos = new ByteArrayOutputStream(), "utf-8");
                    try {
                        exporter.setExporterOutput(exporterOutput);
                        SimpleRtfReportConfiguration configuration = new SimpleRtfReportConfiguration();
                        configuration.setIgnoreHyperlink(true);
                        exporter.setConfiguration(configuration);
                        exporter.exportReport();
                    } finally {
                        exporterOutput.close();
                        exporterOutput = null;
                        exporter = null;
                    }
                    contentType = "text/rtf";
                } else if (type.equalsIgnoreCase("odt")) {
                    JROdtExporter exporter = new JROdtExporter();
                    exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
                    SimpleOutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(
                            bos = new ByteArrayOutputStream());
                    try {
                        exporter.setExporterOutput(exporterOutput);
                        SimpleOdtExporterConfiguration configuration = new SimpleOdtExporterConfiguration();
                        exporter.setConfiguration(configuration);
                        exporter.exportReport();
                    } finally {
                        exporterOutput.close();
                        exporterOutput = null;
                        exporter = null;
                    }
                    contentType = "application/vnd.oasis.opendocument.text";
                } else if (type.equalsIgnoreCase("ods")) {
                    JROdsExporter exporter = new JROdsExporter();
                    exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
                    SimpleOutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(
                            bos = new ByteArrayOutputStream());
                    try {
                        exporter.setExporterOutput(exporterOutput);
                        SimpleOdsExporterConfiguration configuration = new SimpleOdsExporterConfiguration();
                        configuration.setCreateCustomPalette(true);
                        configuration.setKeepWorkbookTemplateSheets(true);
                        exporter.setConfiguration(configuration);
                        exporter.exportReport();
                    } finally {
                        exporterOutput.close();
                        exporterOutput = null;
                        exporter = null;
                    }
                    contentType = "application/vnd.oasis.opendocument.spreadsheet";
                } else if (type.equalsIgnoreCase("xls")) {
                    JRXlsExporter exporter = new JRXlsExporter();
                    exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
                    SimpleOutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(
                            bos = new ByteArrayOutputStream());
                    try {
                        exporter.setExporterOutput(exporterOutput);
                        SimpleXlsExporterConfiguration configuration = new SimpleXlsExporterConfiguration();
                        configuration.setCreateCustomPalette(true);
                        configuration.setKeepWorkbookTemplateSheets(true);
                        exporter.setConfiguration(configuration);
                        exporter.exportReport();
                    } finally {
                        exporterOutput.close();
                        exporterOutput = null;
                        exporter = null;
                    }
                    contentType = "application/vnd.ms-excel";
                } else if (type.equalsIgnoreCase("xlsx")) {
                    JRXlsxExporter exporter = new JRXlsxExporter();
                    exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
                    SimpleOutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(
                            bos = new ByteArrayOutputStream());
                    try {
                        exporter.setExporterOutput(exporterOutput);
                        SimpleXlsxExporterConfiguration configuration = new SimpleXlsxExporterConfiguration();
                        configuration.setCreateCustomPalette(true);
                        configuration.setKeepWorkbookTemplateSheets(true);
                        exporter.setConfiguration(configuration);
                        exporter.exportReport();
                    } finally {
                        exporterOutput.close();
                        exporterOutput = null;
                        exporter = null;
                    }
                    contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                } else if (type.equalsIgnoreCase("docx")) {
                    JRDocxExporter exporter = new JRDocxExporter();
                    exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
                    SimpleOutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(
                            bos = new ByteArrayOutputStream());
                    try {
                        exporter.setExporterOutput(exporterOutput);
                        SimpleDocxExporterConfiguration configuration = new SimpleDocxExporterConfiguration();
                        exporter.setConfiguration(configuration);
                        exporter.exportReport();
                    } finally {
                        exporterOutput.close();
                        exporterOutput = null;
                        exporter = null;
                    }
                    contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
                } else if (type.equalsIgnoreCase("pptx")) {
                    JRPptxExporter exporter = new JRPptxExporter();
                    exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
                    SimpleOutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(
                            bos = new ByteArrayOutputStream());
                    try {
                        exporter.setExporterOutput(exporterOutput);
                        SimplePptxExporterConfiguration configuration = new SimplePptxExporterConfiguration();
                        exporter.setConfiguration(configuration);
                        exporter.exportReport();
                    } finally {
                        exporterOutput.close();
                        exporterOutput = null;
                        exporter = null;
                    }
                    contentType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
                }
                AMedia media = new AMedia(report.getId() + "." + type, type,
                        contentType, bos.toByteArray());
                // media.setContentDisposition(false);
                Filedownload.save(media);
            } catch (Exception ex) {
                throw new EasyPlatformWithLabelKeyException("report.export.error",
                        ex, report.getId(), type);
            } finally {
                Streams.safeClose(bos);
            }
        } else {
            if (type.equalsIgnoreCase("pdf")) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                Exporter exporter = Exporters.getExporter("pdf");
                InputStream is = null;
                try {
                    if (spreadsheet == null) {
                        is = new ByteArrayInputStream((byte[]) reportContent);
                        Importer importer = Importers.getImporter();
                        Book book = importer.imports(is, "");
                        bos = new ByteArrayOutputStream();
                        exporter.export(book, bos);
                    } else
                        exporter.export(spreadsheet.getBook(), bos);
                    Filedownload.save(bos.toByteArray(), "application/pdf", report.getId() + ".pdf");
                } catch (IOException e) {
                    throw new WrongValueException(Contexts.getEvent().getTarget(), Labels.getLabel("explorer.download.error", new Object[]{e.getMessage()}));
                } finally {
                    IOUtils.closeQuietly(bos);
                    if (is != null)
                        IOUtils.closeQuietly(is);
                }
            } else if (type.equalsIgnoreCase("xls") || type.equalsIgnoreCase("xlsx")) {
                Filedownload.save((byte[]) reportContent, "application/vnd.oasis.opendocument.spreadsheet", report.getId() + ".xlsx");
            }
        }
    }

    @Override
    public void print() {
        print(null, false);
    }

    public void print(boolean prompt) {
        print(null, prompt);
    }

    @Override
    public void print(String printer, boolean prompt) {
        if (reportContent == null)
            return;
        if (reportContent instanceof JasperPrint) {
            JasperPrint jasperPrint = (JasperPrint) reportContent;
            // 设置上下左右页边距、纸张大小，注意，单位是 1/10毫米，所以
            StringBuilder sb = new StringBuilder();
            sb.append("zk.printReport('").append(report.getUuid()).append("',")
                    .append(prompt).append(",'{topMargin:")
                    .append(jasperPrint.getTopMargin() * 10).append(",leftMargin:")
                    .append(jasperPrint.getLeftMargin() * 10)
                    .append(",bottomMargin:")
                    .append(jasperPrint.getBottomMargin() * 10)
                    .append(",rightMargin:")
                    .append(jasperPrint.getRightMargin() * 10)
                    .append(",orientation:")
                    .append(jasperPrint.getOrientationValue().getValue())
                    .append(",paperWidth:").append(jasperPrint.getPageWidth() * 10)
                    .append(",paperHeight:")
                    .append(jasperPrint.getPageHeight() * 10);
            if (!Strings.isBlank(printer))
                sb.append(",printer:\"").append(printer).append("\"");
            sb.append("}')");
            Clients.evalJavaScript(sb.toString());
        }
    }

    @Override
    public void reload() {
        ComponentService cs = ServiceLocator
                .lookup(ComponentService.class);
        if (report.isShowProgress() == null || report.isShowProgress()) {//显示进度
            mc = new ProgressDialog(report.getPage());
            Contexts.subscribe(report.getEntity(), this);
        }
        IResponseMessage<?> resp = cs.doReport(new DoReportRequestMessage(
                mainTaskHandler.getId(), new ReportVo(report.getId(), report
                .getEntity(), report.getType(), report.getFrom(),
                report.getState(), report.getIds(), report
                .getListOrder(), report.getInit(), report
                .isShowProgress())));
        if (resp.isSuccess()) {
            if (resp.getBody() != null) {
                if (mc != null) {
                    mc.display(new EndVo());
                    Contexts.unsubscribe(report.getEntity(), this);
                    mc = null;
                }
                reportContent = resp.getBody();
                showReport();
            }
        } else {
            if (mc != null) {
                mc.display(new EndVo());
                Contexts.unsubscribe(report.getEntity(), this);
                mc = null;
            }
            throw new BackendException(resp);
        }

    }

    protected void showReport() {
        if (reportContent instanceof JasperPrint) {
            ByteArrayOutputStream bos = null;
            try {
                if (report.getType().equals("html")) {
                    HtmlExporter exporter = new HtmlExporter();
                    exporter.setExporterInput(new SimpleExporterInput((JasperPrint) reportContent));
                    SimpleHtmlExporterConfiguration configuration = new SimpleHtmlExporterConfiguration();
                    configuration.setFlushOutput(true);
                    exporter.setConfiguration(configuration);
                    bos = new ByteArrayOutputStream();
                    SimpleHtmlExporterOutput exporterOutput = new SimpleHtmlExporterOutput(
                            bos, "utf-8");
                    try {
                        exporterOutput.setImageHandler(new WebHtmlResourceHandler(
                                IMAGE_PATTERN));
                        exporter.setExporterOutput(exporterOutput);
                        exporter.exportReport();
                        report.setContent(new AMedia("", "html", "text/html", bos
                                .toByteArray()));
                    } finally {
                        exporterOutput.close();
                        exporterOutput = null;
                        exporter = null;
                    }
                    Sessions.getCurrent().setAttribute(
                            ImageServlet.DEFAULT_JASPER_PRINT_SESSION_ATTRIBUTE,
                            reportContent);
                } else {
                    JRPdfExporter exporter = new JRPdfExporter();
                    exporter.setExporterInput(new SimpleExporterInput((JasperPrint) reportContent));
                    bos = new ByteArrayOutputStream();
                    SimpleOutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(
                            bos);
                    try {
                        exporter.setExporterOutput(exporterOutput);
                        exporter.exportReport();
                        report.setContent(new AMedia("", "pdf", "application/pdf",
                                bos.toByteArray()));
                    } finally {
                        exporterOutput.close();
                        exporterOutput = null;
                        exporter = null;
                    }
                }
            } catch (Exception ex) {
                throw new EasyPlatformWithLabelKeyException("report.run.error", ex,
                        report.getId());
            } finally {
                Streams.safeClose(bos);
            }
        } else {
            if ("pdf".equalsIgnoreCase(report.getType())) {
                InputStream is = null;
                ByteArrayOutputStream bos = null;
                try {
                    is = new ByteArrayInputStream((byte[]) reportContent);
                    Importer importer = Importers.getImporter();
                    Book book = importer.imports(is, "");
                    if (book.getNumberOfSheets() > 1)
                        book.getInternalBook().deleteSheet(book.getSheetAt(0).getInternalSheet());
                    bos = new ByteArrayOutputStream();
                    Exporter exporter = Exporters.getExporter("pdf");
                    exporter.export(book, bos);
                    AMedia media = new AMedia(report.getIds(), "pdf", "application/pdf", bos.toByteArray());
                    if (pdfviewer == null) {
                        pdfviewer = new Pdfviewer();
                        if (!Strings.isBlank(report.getWidth()))
                            pdfviewer.setWidth(report.getWidth());
                        if (!Strings.isBlank(report.getHeight()))
                            pdfviewer.setHeight(report.getHeight());
                        if (!Strings.isBlank(report.getZclass()))
                            pdfviewer.setSclass(report.getZclass());
                        if (!Strings.isBlank(report.getSclass()))
                            pdfviewer.setSclass(report.getSclass());
                        if (!Strings.isBlank(report.getVflex()))
                            pdfviewer.setVflex(report.getVflex());
                        if (!Strings.isBlank(report.getHflex()))
                            pdfviewer.setHflex(report.getHflex());
                        pdfviewer.setZoom(report.getZoom());
                        pdfviewer.setId(report.getId());
                        Components.replace(report, pdfviewer);
                    }
                    pdfviewer.setContent(media);
                    //pdfviewer.setSrc("/servlets/download?id=/WEB-INF/temp/" + tempFile.getName());
                } catch (Exception e) {
                    if (log.isErrorEnabled())
                        log.error("showReport", e);
                    throw new EasyPlatformWithLabelKeyException("report.run.error", e,
                            report.getId());
                } finally {
                    IOUtils.closeQuietly(is);
                    IOUtils.closeQuietly(bos);
                }
            } else {
                if (spreadsheet == null) {
                    spreadsheet = new Spreadsheet();
                    spreadsheet.setShowAddColumn(false);
                    spreadsheet.setShowContextMenu(false);
                    spreadsheet.setShowFormulabar(false);
                    spreadsheet.setShowToolbar(false);
                    spreadsheet.setKeepCellSelection(false);
                    spreadsheet.setIgnoreAutoHeight(true);
                    spreadsheet.setHidecolumnhead(true);
                    spreadsheet.setHiderowhead(true);
                    if (!Strings.isBlank(report.getWidth()))
                        spreadsheet.setWidth(report.getWidth());
                    if (!Strings.isBlank(report.getHeight()))
                        spreadsheet.setHeight(report.getHeight());
                    if (!Strings.isBlank(report.getZclass()))
                        spreadsheet.setSclass(report.getZclass());
                    if (!Strings.isBlank(report.getSclass()))
                        spreadsheet.setSclass(report.getSclass());
                    if (!Strings.isBlank(report.getVflex()))
                        spreadsheet.setVflex(report.getVflex());
                    if (!Strings.isBlank(report.getHflex()))
                        spreadsheet.setHflex(report.getHflex());
                    Components.replace(report, spreadsheet);
                }
                InputStream is = null;
                //OutputStream os = null;
                try {
                    //IOUtils.write((byte[]) reportContent, os = new FileOutputStream("f:\\test.xls"));
                    is = new ByteArrayInputStream((byte[]) reportContent);
                    Importer importer = Importers.getImporter();
                    Book book = importer.imports(is, "");
                    if (book.getNumberOfSheets() > 1) {
                        spreadsheet.setShowSheetbar(true);
                    } else {
                        Sheet sheet = book.getSheetAt(0);
                        if (sheet.getInternalSheet().getEndColumnIndex() > 0)
                            spreadsheet.setMaxVisibleColumns(sheet.getInternalSheet().getEndColumnIndex() + 1);
                        spreadsheet.setMaxVisibleRows(sheet.getInternalSheet().getEndRowIndex() + 1);
                    }
                    spreadsheet.setBook(book);
                    //spreadsheet.setSrc("/WEB-INF/demo_sample.xlsx");
                } catch (Exception e) {
                    if (log.isErrorEnabled())
                        log.error("showReport", e);
                    throw new EasyPlatformWithLabelKeyException("report.run.error", e,
                            report.getId());
                } finally {
                    IOUtils.closeQuietly(is);
                    // IOUtils.closeQuietly(os);
                }
            }
        }
    }

    @Override
    public void setType(String type) {
        if (!report.getType().equals(type)) {
            report.setType(type);
            if (reportContent != null)
                showReport();
        }
    }

    @Override
    public Component getComponent() {
        return report;
    }

    @Override
    public void onEvent(Event evt) {
        Object msg = evt.getData();
        mc.display(msg);
        if (msg instanceof EndVo || msg instanceof ErrorVo) {
            Contexts.unsubscribe(report.getEntity(), this);
            if (msg instanceof EndVo) {
                reportContent = ((EndVo) msg).getResult();
                if (reportContent != null)
                    showReport();
            }
            mc = null;
        }
    }
}
